package org.force4spring.support;

import java.text.DateFormat;
import java.text.ParseException;

import org.force4spring.ForceTemplate;
import org.springframework.util.StringUtils;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;

import com.sforce.soap.partner.DescribeLayout;
import com.sforce.soap.partner.DescribeLayoutComponent;
import com.sforce.soap.partner.DescribeLayoutItem;
import com.sforce.soap.partner.DescribeLayoutResult;
import com.sforce.soap.partner.DescribeLayoutRow;
import com.sforce.soap.partner.DescribeLayoutSection;
import com.sforce.soap.partner.Field;
import com.sforce.soap.partner.LayoutComponentType;

public class ForcePageValidator implements Validator {
	private ForceTemplate forceTemplate;
	/**
	 * @return the forceTemplate
	 */
	public ForceTemplate getForceTemplate() {
		return forceTemplate;
	}

	/**
	 * @param forceTemplate
	 *            the forceTemplate to set
	 */
	public void setForceTemplate(ForceTemplate forceTemplate) {
		this.forceTemplate = forceTemplate;
	}

	/**
	 * @see org.springframework.validation.Validator#supports(java.lang.Class)
	 */
	public boolean supports(Class type) {
		return SObjectDynaBean.class.isAssignableFrom(type);
	}

	/**
	 * @see org.springframework.validation.Validator#validate(java.lang.Object,
	 *      org.springframework.validation.Errors)
	 */
	public void validate(Object command, Errors errors) {
		SObjectDynaBean bean = (SObjectDynaBean)command;
		SObjectDynaClass dynaClass = (SObjectDynaClass)bean.getDynaClass();
		String recordTypeId = (String)bean.get(StandardFields.RECORD_TYPE_ID);
		String[] recordTypeIds = null;
		if (recordTypeId != null) {
			recordTypeIds = new String[]{recordTypeId};
		}
		DescribeLayoutResult result = forceTemplate.describeLayout(bean.getDynaClass().getName(), recordTypeIds);
		DescribeLayout layout = result.getLayouts(0);
		for (DescribeLayoutSection section : layout.getEditLayoutSections()) {
			for (DescribeLayoutRow row : section.getLayoutRows()) {
				for (DescribeLayoutItem item : row.getLayoutItems()) {
					for (DescribeLayoutComponent comp : item.getLayoutComponents()) {
						if (LayoutComponentType.Field.equals(comp.getType())) {
							String fieldName = comp.getValue();
							Field field = dynaClass.getField(fieldName);
							Object value = bean.get(fieldName);
							if (item.isRequired()) {
								validateRequired(field, value, errors);
							}
							validateType(field,value,errors);
						}
					}
				}
			}
		}
	}

	protected void validateRequired(Field field, Object value, Errors errors) {
		String errorCode = "errors.field.required";
		String property = StringUtils.uncapitalize(field.getName());
		if ((value == null) || (value.toString().trim().length() == 0)) {
			errors.rejectValue(property, errorCode, new String[] { field
					.getLabel() }, null);
		}
	}

	protected void validateType(Field field, Object value, Errors errors) {
		if (value == null) {
			return;
		}

		String property = StringUtils.uncapitalize(field.getName());
		if (field.getType().getValue() == "int") {
			try {
				Integer.parseInt(value.toString());
			} catch (NumberFormatException nfe) {
				errors.rejectValue(property, "errors.field.integer",
						new String[] { field.getLabel() }, null);
			}
		}

		if (field.getType().getValue() == "double") {
			try {
				Double.parseDouble(value.toString());
			} catch (NumberFormatException nfe) {
				errors.rejectValue(property, "errors.field.double",
						new String[] { field.getLabel() }, null);
			}
		}

		if (field.getType().getValue() == "date") {
			try {
				DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT);
				df.parse(value.toString());
			} catch (ParseException pe) {
				errors.rejectValue(property, "errors.field.date",
						new String[] { field.getLabel() }, null);
			}
		}

		if (field.getType().getValue() == "dateTime") {
			try {
				DateFormat df = DateFormat.getDateTimeInstance(
						DateFormat.SHORT, DateFormat.SHORT);
				df.parse(value.toString());
			} catch (ParseException pe) {
				errors.rejectValue(property, "errors.field.dateTime",
						new String[] { field.getLabel() }, null);
			}
		}

		if (field.getType().getValue() == "email") {
			if (!isValidEmail(value.toString())) {
				errors.rejectValue(property, "errors.field.email",
						new String[] { field.getLabel() }, null);
			}
		}
	}

	private boolean isValidEmail(String email) {
		if (email.indexOf(' ') > -1) {
			return false;
		}

		if (email.indexOf('@') < 0) {
			return false;
		}

		return true;
	}
}
